
#include "superficie.h"

#include "coord.h"
#include "curva.h"
#include "bezier.h"

#include <cassert>


Superficie::Superficie(const Curva &curvaRevolucion, const Bezier &curvaPerfil) :
   curvaRevolucion(curvaRevolucion),
   curvaPerfil(*new Bezier(curvaPerfil)) {}


   Superficie::~Superficie() {
      delete &curvaPerfil;
   }

   /*
    * Discretiza la curva de Revolucion y para cada punto discretizado
    * calcula la discretizacion (rotada) de la curva de Perfil
    *
    * La superficie estara centrada en el centro de la curva de Revolucion.
    * Su altura coincidira con la altura de la curva de Perfil, coincidiendo
    * la base del Perfil con el plano z = 0.
    *
    * Precondicion:
    *    0 <= u <= 1
    *    0 <= v <= 1
    *
    * */
   Coord Superficie::operator()(float u, float v) const {

      // Discretizamos la curva de Revolucion segun el paso u
      // Se obtiene tanto el punto P discretizado como la direccion
      // normal P_Direccion sobre dicho punto.
      //
      // Donde la Direccion es de la forma (x, y, 0)
      //
      Coord p = curvaRevolucion(u);
      Coord p_direccion = (p - curvaRevolucion.centro()).normalizar();
      assert(p.z() == 0);
      assert(p_direccion.z() == 0);

      // Coordenada de Referencia
      Coord referencia(curvaPerfil(0.0).x(), 0.0, curvaPerfil(1.0).z());

      // Discretizamos la curva de Perfil segun el paso v
      // Se realiza una traslacion respecto a la coordenada de Referencia
      // Obteniendo 
      //    - distancia radial: distancia desde el centro de rotacion
      //                        de la curva de Revolucion hasta el punto final
      //
      //    - altura Z: Altura sobre la coordenada z
      //
      Coord b = curvaPerfil(v) - referencia;
      assert(b.y() == 0.0);

      // distanciaRadial = b.x();
      // altura = b.z();

      p += p_direccion * b.x();
      p.z() = b.z();

      return p;
   }

Coord Superficie::eval(float u, float v) const { 
   return this->operator()(u, v); 
}

Coord Superficie::normal(float u, float v) const {
   Coord q = curvaRevolucion.tangente(u);

   Coord tangente = curvaPerfil.tangente(v);
   assert(tangente.y() == 0.0);

   Coord p = (curvaRevolucion(u) - curvaRevolucion.centro());

   p *= tangente.x();
   p.z() = tangente.z();

   return p.cross(q).normalizar();
}


float Superficie::altura() const {
   return curvaPerfil(0.0).z() - curvaPerfil(1.0).z();
}
